home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
EnigmA Amiga Run 1996 June
/
EnigmA AMIGA RUN 08 (1996)(G.R. Edizioni)(IT)[!][issue 1996-06][EARSAN CD VII].iso
/
earcd
/
utmisc1
/
chktex.lha
/
chktex
/
ChkTeX.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-04-30
|
24KB
|
853 lines
/*
* ChkTeX v1.4, finds typographic errors in (La)TeX files.
* Copyright (C) 1995-96 Jens T. Berger Thielemann
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Contact the author at:
* Jens Berger
* Spektrumvn. 4
* N-0666 Oslo
* Norway
* E-mail: <jensthi@ifi.uio.no>
*
*
*/
#include "ChkTeX.h"
struct Stack
CharStack = {0L},
InputStack = {0L},
EnvStack = {0L};
/************************************************************************/
const UBYTE BrOrder [NUMBRACKETS + 1] = "()[]{}";
/******************** VARS CHANGED RUNTIME ******************************/
ULONG Brackets [NUMBRACKETS];
ULONG ErrPrint, /* # errors printed */
WarnPrint, /* # warnings printed */
UserSupp; /* # user suppressed warnings */
enum ItState /* Are we doing italics? */
ItState;
/************************************************************************/
/*
* Have to do things this way, to ease some checking throughout the
* program.
*/
NEWBUF(TmpBuffer, BUFSIZ);
NEWBUF(ReadBuffer, BUFSIZ);
const STRPTR
Banner =
"ChkTeX v1.4 - Copyright 1995-96 Jens T. Berger Thielemann.\n"
#ifdef __MSDOS__
"MS-DOS port by Bj\\o rn Ove Thue\n"
#endif
"\n"
,
BigBanner =
"ChkTeX comes with ABSOLUTELY NO WARRANTY; details on this and\n"
"distribution conditions in the GNU General Public License file.\n"
"Type \"ChkTeX -h\" for help, \"ChkTeX -i\" for distribution info.\n"
"To contact the author, write to:\n"
"Jens Berger, Spektrumvn. 4, N-0666 Oslo, Norway.\n"
"E-mail: <jensthi@ifi.uio.no>\n"
"Press " STDIN_BREAK " to abort stdin input.\n",
Distrib =
"\n"
"This program is free software; you can redistribute it and/or modify\n"
"it under the terms of the GNU General Public License as published by\n"
"the Free Software Foundation; either version 2 of the License, or\n"
"(at your option) any later version.\n"
"\n"
"This program is distributed in the hope that it will be useful,\n"
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
"GNU General Public License for more details.\n"
"\n"
"You should have received a copy of the GNU General Public License\n"
"along with this program; if not, write to the Free Software\n"
"Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n"
"\n",
OnText =
"On",
OffText =
"Off",
HelpText =
"\n"
"\n"
" Usage of ChkTeX v1.4\n"
" ~~~~~~~~~~~~~~~~~~~~\n"
"\n"
" Template\n"
" ~~~~~~~~\n"
"chktex [-hiqr] [-v[0|1|2]] [-l <rcfile>] [-[w|e|m|n] <[0-24]>]\n"
" [-d <number>] [-o <outputfile>] [-b[0|1]] [-t[0|1]]\n"
" [-x[0|1]] [-g[0|1]]\n"
"\n"
"----------------------------------------------------------------------\n"
" Description of options:\n"
" ~~~~~~~~~~~~~~~~~~~~~~~\n"
"Misc. options\n"
"~~~~~~~~~~~~~\n"
" -h --help : This text.\n"
" -i --license : Show distribution information\n"
" -l --localrc : Read local .chktexrc formatted file.\n"
" -d --debug : Debug information. Give it a number.\n"
" -r --reset : Reset settings to default.\n"
"\n"
"Muting warning messages:\n"
"~~~~~~~~~~~~~~~~~~~~~~~~\n"
" -w --warnon : Makes msg # given a warning and turns it on.\n"
" -e --erroron : Makes msg # given an error and turns it on.\n"
" -m --msgon : Makes msg # given a message and turns it on.\n"
" -n --nowarn : Mutes msg # given.\n"
"\n"
"Output control flags:\n"
"~~~~~~~~~~~~~~~~~~~~~\n"
" -v --verbosity : How errors are displayed.\n"
" Default 1, 0=Less, 2=Fancy, 3=lacheck.\n"
" -s --splitchar : String used to split fields when doing -v0\n"
" -o --output : Redirect error report to a file.\n"
" -q --quiet : Shuts up about version information.\n"
" -p --pseudoname: Input file-name when reporting.\n"
" -f --format : Format to use for output\n"
"\n"
"Boolean switches (1 -> enables / 0 -> disables):\n"
"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
" -b --backup : Backup output file.\n"
" -x --wipeverb : Ignore contents of `\\verb' commands.\n"
" -g --globalrc : Read global .chktexrc file.\n"
" -I --inputfiles: Execute \\input statements.\n"
" -H --headererr : Show errors found in front of \\begin{document}\n"
"\n"
"----------------------------------------------------------------------\n"
"If no LaTeX files are specified on the command line, we will read from\n"
"stdin. For explanation of warning/error messages, please consult the\n"
"main document ChkTeX.dvi or ChkTeX.ps.\n";
UBYTE EmptyString [] = "";
/*
* Options we will set.
*
*/
BOOL
GlobalRC = TRUE, WipeVerb = TRUE, BackupOut = TRUE,
Quiet = FALSE, LicenseOnly = FALSE, UsingStdIn = FALSE,
InputFiles = TRUE, HeadErrOut = TRUE;
LONG DebugLevel = 0;
FILE *OutputFile = NULL;
STRPTR OutputName = EmptyString, PseudoInName = NULL;
UBYTE VerbNormal [] =
"%k %n in %f line %l: %m\n"
"%r%s%t\n"
"%u\n";
STRPTR Delimit = ":";
STRPTR OutputFormat = VerbNormal;
STRPTR PrgName;
/*
* End of config params.
*/
/*
* Duplicates all arguments, and appends an asterix to each of them.
*/
static void AddStars(struct WordList *wl)
{
ULONG Count, CmdLen;
STRPTR Data;
for(Count = 0L;
Count < wl->Stack.Used;
Count++)
{
Data = wl->Stack.Data[Count];
CmdLen = strlen(Data);
if(Data[CmdLen - 1] != '*')
{
strcpy(TmpBuffer, Data);
strcat(TmpBuffer, "*");
InsertWord(TmpBuffer, wl);
}
}
}
int main(int argc, char **argv)
{
int retval = EXIT_FAILURE, CurArg = 0L;
ULONG Count;
BOOL StdInUse = FALSE;
STRPTR NameMatch = EmptyString,
TmpPtr;
BOOL QwkAbbrev;
#ifdef __LOCALIZED
InitStrings();
#endif
#ifdef AMIGA
if(_WBenchMsg)
{
if(argv = WB2Argv(_WBenchMsg, W2A_LOWER))
argc = CountArgv(argv);
else
exit(EXIT_FAILURE);
}
#endif
OutputFile = stdout;
PrgName = argv[0];
SetupVars();
InsertWord("", &CmdLine);
InsertWord("", &TeXInputs);
ReadRC(ConfigFile);
if(CmdLine.Stack.Used)
{
ParseArgs(CmdLine.Stack.Used, (STRPTR *) CmdLine.Stack.Data);
CmdLine.Stack.Used = 1L;
}
if((CurArg = ParseArgs((ULONG) argc, argv)))
{
if(CmdLine.Stack.Used)
{
ParseArgs(CmdLine.Stack.Used, (STRPTR *) CmdLine.Stack.Data);
CmdLine.Stack.Used = 1L;
}
if(!Quiet || LicenseOnly)
fprintf(stderr, Banner);
if(CurArg == argc)
UsingStdIn = TRUE;
if((UsingStdIn && !Quiet) || LicenseOnly)
fprintf(stderr, BigBanner);
if(LicenseOnly)
fprintf(stderr, Distrib);
else
{
for(Count = 0L;
Count < Abbrev.Stack.Used;
Count++)
{
QwkAbbrev = TRUE;
TmpPtr = Abbrev.Stack.Data[Count];
strupr(TmpPtr);
/* Sort out those needing slow searching into a separate array. */
while(QwkAbbrev && *TmpPtr)
{
QwkAbbrev &= isalpha(*TmpPtr) ||
((*TmpPtr == '.') && !TmpPtr[1]);
TmpPtr++;
}
if(!QwkAbbrev)
{
TmpPtr = Abbrev.Stack.Data[Count];
InsertWord(TmpPtr, &SlowAbbrev);
}
}
AddStars(&VerbEnvir);
AddStars(&MathEnvir);
if(DebugLevel)
ShowIntStatus();
if(OpenOut())
{
for(;;)
{
for(Count = 0; Count < NUMBRACKETS; Count++)
Brackets[Count] = 0L;
ItState = itOff;
AtLetter = MathMode = VerbMode = FALSE;
ErrPrint = WarnPrint = UserSupp = 0L;
InHeader = TRUE;
if(UsingStdIn) {
if(StdInUse)
break;
else {
StdInUse = TRUE;
PushFile("stdin", stdin, &InputStack);
}
} else {
if((CurArg <= argc) || NameMatch) {
ifn(NameMatch = MatchFileName(NULL)) {
if(CurArg < argc)
NameMatch = MatchFileName(argv[CurArg++]);
}
if(!PushFileName(NameMatch, &InputStack))
break;
}
}
if(StkTop(&InputStack) && OutputFile)
{
while(!ferror(OutputFile) && StkTop(&InputStack) &&
!ferror(CurStkFile(&InputStack)) &&
FGetsStk(ReadBuffer, BUFSIZ-1, &InputStack))
{
/* Make all spaces ordinary spaces */
strrep(ReadBuffer, '\n', ' ');
strrep(ReadBuffer, '\r', ' ');
strrep(ReadBuffer, '\t', ' ');
strcat(ReadBuffer, " ");
FindErr(ReadBuffer, CurStkLine(&InputStack));
}
PrintStatus(CurStkLine(&InputStack));
retval = EXIT_SUCCESS;
}
}
}
}
}
exit(retval);
}
/*
* Opens the output file handle & possibly renames
*/
BOOL OpenOut(void)
{
BOOL Success = TRUE;
if(*OutputName)
{
if(BackupOut && fexists(OutputName))
{
strncpy(TmpBuffer, OutputName, BUFSIZ - 4);
strcat(TmpBuffer, ".bak");
if(fexists(TmpBuffer))
remove(TmpBuffer);
if(!rename(OutputName, TmpBuffer))
PrintPrgErr(pmRename,
OutputName, TmpBuffer);
else
{
PrintPrgErr(pmRenameErr,
OutputName, TmpBuffer);
Success = FALSE;
}
}
if(Success)
{
ifn(OutputFile = fopen(OutputName, "w"))
{
PrintPrgErr(pmOutOpen);
Success = FALSE;
}
}
}
else
OutputFile = stdout;
return(Success);
}
#define BOOLDISP(var) ((var)? OnText : OffText)
#define SHOWSTAT(text, arg) fprintf(stderr, "\t" text ": %s\n", arg)
#define BOOLSTAT(name, var) SHOWSTAT(name, BOOLDISP(var))
#define SHOWWORD(text, list) fprintf(stderr, "\n" text "\n"); \
for(Cnt = 0L; \
Cnt < list.Stack.Used; \
Cnt++) \
fprintf(stderr, "\t%s\n", (STRPTR) list.Stack.Data[Cnt])
#define SHOWSTR(text, arg) fprintf(stderr, "%s:\n\t%s\n", text, arg);
/*
* Prints some of the internal flags; mainly for debugging purposes
*/
void ShowIntStatus(void)
{
ULONG Cnt;
STRPTR String;
fprintf(stderr, "There are %d warnings/error messages available:\n",
emMaxFault - emMinFault - 1);
for(Cnt = emMinFault + 1; Cnt < emMaxFault; Cnt++)
{
switch(LaTeXMsgs[Cnt].Type)
{
case etWarn: String = "Warning"; break;
case etErr: String = "Error"; break;
case etMsg: String = "Message"; break;
default: String = ""; break;
}
fprintf(stderr, "Number: %2ld, Type: %s, Status: %s\n"
"\tText: %s\n\n",
Cnt, String,
BOOLDISP(LaTeXMsgs[Cnt].InUse),
LaTeXMsgs[Cnt].Message);
}
SHOWWORD("Abbreviations searched for:", Abbrev);
SHOWWORD("`Silent' commands:", Silent);
SHOWWORD("`Linked' commands:", Linker);
SHOWWORD("Accenting commands needing `\\i' and `\\j':", IJAccent);
SHOWWORD("Italizing commands:", Italic);
SHOWWORD("User patterns:", UserWarn);
SHOWWORD("Slow-mode abbrevs:", SlowAbbrev);
SHOWWORD("Wipe arguments of:", WipeArg);
SHOWWORD("Ignore contents of environments:", VerbEnvir);
SHOWWORD("Treat contents of environments as math:", MathEnvir);
SHOWWORD("Hyphen dash len:", HyphDash);
SHOWWORD("Numeric dash len:", NumDash);
SHOWWORD("Interword dash len:", WordDash);
SHOWWORD("\\cdots items:", CenterDots);
SHOWWORD("\\ldots items:", LowDots);
SHOWWORD("Math roman:", MathRoman);
SHOWWORD("TeX input paths:", TeXInputs);
SHOWWORD("Available output formats", OutFormat);
SHOWWORD("Post-link commands", PostLink);
SHOWSTR("Outputformat", OutputFormat);
SHOWSTR("VerbClear", VerbClear);
fprintf(stderr, "Current flags include:\n");
BOOLSTAT("Read global resource", GlobalRC);
BOOLSTAT("Wipe verbose commands", WipeVerb);
BOOLSTAT("Backup outfile", BackupOut);
BOOLSTAT("Quiet mode", Quiet);
BOOLSTAT("Show license", LicenseOnly);
BOOLSTAT("Use stdin", UsingStdIn);
BOOLSTAT("\\input files", InputFiles);
BOOLSTAT("Output header errors", HeadErrOut);
}
/*
* Resets all stacks.
*
*/
static void ResetStacks(void)
{
Silent.Stack.Used = 0;
Abbrev.Stack.Used = 0;
SlowAbbrev.Stack.Used = 0;
Linker.Stack.Used = 0;
IJAccent.Stack.Used = 0;
Italic.Stack.Used = 0;
ItalCmd.Stack.Used = 0;
UserWarn.Stack.Used = 0;
CmdLine.Stack.Used = 0;
PostLink.Stack.Used = 0;
WipeArg.Stack.Used = 0;
VerbEnvir.Stack.Used = 0;
MathEnvir.Stack.Used = 0;
MathRoman.Stack.Used = 0;
TeXInputs.Stack.Used = 0;
HyphDash.Stack.Used = 0;
NumDash.Stack.Used = 0;
WordDash.Stack.Used = 0;
CenterDots.Stack.Used = 0;
LowDots.Stack.Used = 0;
OutFormat.Stack.Used = 0;
}
/*
* Resets all flags (not wordlists) to their default values. Sets
* Outputfile to stdout.
*
*/
static void ResetSettings(void)
{
GlobalRC = TRUE;
WipeVerb = TRUE;
BackupOut = TRUE;
Quiet = FALSE;
LicenseOnly = FALSE;
UsingStdIn = FALSE;
DebugLevel = 0;
InputFiles = TRUE;
HeadErrOut = TRUE;
OutputFormat = VerbNormal;
if(OutputFile != stdout)
{
fclose(OutputFile);
OutputFile = stdout;
}
OutputName = EmptyString;
}
/*
* Reads a numerical argument from the argument. Supports concatenation
* of arguments (main purpose)
*/
int ParseNumArg(LONG *Dest, /* Where to put the value */
LONG Default, /* Value to put in if no in argue */
STRPTR *Argument) /* optarg or similar */
{
if(Argument && *Argument && isdigit(**Argument))
*Dest = strtol(*Argument, Argument, 10);
else
*Dest = Default;
return(ShiftArg(Argument));
}
/*
* Same as above; however, will toggle the boolean if user doesn't
* supply value
*/
int ParseBoolArg(BOOL *Dest, /* Boolean value */
STRPTR *Argument) /* optarg or similar */
{
LONG D = *Dest? 1L : 0L;
int Retval;
Retval = ParseNumArg(&D, *Dest? 0L : 1L, Argument);
*Dest = D ? TRUE : FALSE;
return(Retval);
}
/*
* Returns the first character in the string passed, updates the
* string pointer, if the string is non-empty.
*
* 0 if the string is empty.
*/
int ShiftArg(STRPTR *Argument) /* optarg or similar */
{
if(Argument && *Argument && **Argument)
return(*((STRPTR) (*Argument)++));
else
return 0;
}
/*
* Parses an argv similar array.
*/
int ParseArgs(int argc, char **argv)
{
/* Needed for option parsing. */
static const
struct option long_options[] =
{
{"help", no_argument, 0L, 'h'},
{"localrc", required_argument, 0L, 'l'},
{"output", required_argument, 0L, 'o'},
{"warnon", required_argument, 0L, 'w'},
{"erroron", required_argument, 0L, 'e'},
{"msgon", required_argument, 0L, 'm'},
{"nowarn", required_argument, 0L, 'n'},
{"verbosity", optional_argument, 0L, 'v'},
{"debug", required_argument, 0L, 'd'},
{"reset", no_argument, 0L, 'r'},
{"quiet", no_argument, 0L, 'q'},
{"license", no_argument, 0L, 'i'},
{"splitchar", required_argument, 0L, 's'},
{"format", required_argument, 0L, 'f'},
{"pseudoname", required_argument, 0L, 'p'},
{"inputfiles", optional_argument, 0L, 'I'},
{"backup", optional_argument, 0L, 'b'},
{"globalrc", optional_argument, 0L, 'g'},
{"wipeverb", optional_argument, 0L, 'x'},
{"tictoc", optional_argument, 0L, 't'},
{"headererr", optional_argument, 0L, 'H'},
{0L, 0L, 0L, 0L}
};
int option_index = 0L, c, nextc, ErrType = 0;
int Retval = FALSE, InUse;
BOOL Success = FALSE, Foo;
LONG Err, Verb = 1;
enum
{
aeNoErr = 0,
aeArg, /* missing/bad required argument */
aeOpt, /* unknown option returned */
aeHelp, /* just a call for help */
aeMem /* no memory */
} ArgErr = aeNoErr;
optind = 0;
while(!ArgErr &&
((c = getopt_long((int) argc, argv,
"b::d:e:f:g::hH::I::il:m:n:o:p:qrs:t::v::w:x::",
long_options,
&option_index)) != EOF))
{
while(c) {
nextc = 0;
switch(c)
{
case 's':
ifn(Delimit = strdup(optarg)) {
PrintPrgErr(pmStrDupErr);
ArgErr = aeMem;
}
break;
case 'p':
ifn(PseudoInName = strdup(optarg)) {
PrintPrgErr(pmStrDupErr);
ArgErr = aeMem;
}
break;
case 'd':
nextc = ParseNumArg(&DebugLevel, 0, &optarg);
break;
case 'i':
LicenseOnly = TRUE;
nextc = ShiftArg(&optarg);
break;
case 'q':
Quiet = TRUE;
nextc = ShiftArg(&optarg);
break;
/* Variation of Duff's device... */
do {
case 'w':
ErrType = etWarn;
InUse = TRUE;
break;
case 'e':
ErrType = etErr;
InUse = TRUE;
break;
case 'm':
ErrType = etMsg;
InUse = TRUE;
break;
case 'n':
InUse = FALSE;
} while(FALSE);
nextc = ParseNumArg(&Err, -1, &optarg);
if(betw(emMinFault, Err, emMaxFault)) {
LaTeXMsgs[Err].Type = ErrType;
LaTeXMsgs[Err].InUse = InUse;
} else {
ArgErr = aeOpt;
PrintPrgErr(pmWarnNumErr);
}
break;
case 'g':
nextc = ParseBoolArg(&GlobalRC, &optarg);
if(!GlobalRC) {
ResetStacks();
}
break;
case 'r':
ResetSettings();
nextc = ShiftArg(&optarg);
break;
case 'l':
if(optarg)
ReadRC(optarg);
break;
case 'f':
ifn(OutputFormat = strdup(optarg))
{
PrintPrgErr(pmStrDupErr);
ArgErr = aeMem;
}
break;
case 'v':
nextc = ParseNumArg(&Verb, 2, &optarg);
if(Verb < (LONG) OutFormat.Stack.Used)
OutputFormat = strdup(OutFormat.Stack.Data[Verb]);
else
{
PrintPrgErr(pmVerbLevErr);
ArgErr = aeArg;
}
break;
case 'o':
if(optarg)
{
if(*OutputName)
{
PrintPrgErr(pmOutTwice);
ArgErr = aeOpt;
}
else
{
ifn(OutputName = strdup(optarg))
{
PrintPrgErr(pmStrDupErr);
ArgErr = aeMem;
}
}
}
break;
case 't':
nextc = ParseBoolArg(&Foo, &optarg);
break;
case 'x':
nextc = ParseBoolArg(&WipeVerb, &optarg);
break;
case 'b':
nextc = ParseBoolArg(&BackupOut, &optarg);
break;
case 'I':
nextc = ParseBoolArg(&InputFiles, &optarg);
break;
case 'H':
nextc = ParseBoolArg(&HeadErrOut, &optarg);
break;
case 'h':
case '?':
default:
ArgErr = aeHelp;
break;
}
c = nextc;
}
}
if((argc > optind) && !strcmp(argv[optind], "?"))
ArgErr = aeHelp;
if(ArgErr)
{
fputs(Banner, stderr);
fputs(BigBanner, stderr);
fputs(HelpText, stderr);
Success = FALSE;
}
else
Success = TRUE;
if(Success)
Retval = optind;
return(Retval);
}
/*
* Outputs a program error.
*/
void PrintPrgErr(enum PrgErrNum Error, ...)
{
STRPTR Type;
va_list MsgArgs;
if(betw(pmMinFault, Error, pmMaxFault)) {
switch(PrgMsgs[Error].Type) {
case etWarn:
Type = "WARNING";
break;
case etMsg:
Type = "NOTE";
break;
default:
case etErr:
Type = "ERROR";
break;
}
fprintf(stderr, "%s: %s -- ", PrgName, Type);
va_start(MsgArgs, Error);
vfprintf(stderr, PrgMsgs[Error].Message, MsgArgs);
va_end(MsgArgs);
fputc('\n', stderr);
}
}